import socket
import shlex
import xml.dom.minidom


from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import urllib
import copy
import os
import urlparse
import cgi

import pdb

import pexpect
import re


from my_sa import *


#HOST = "138.202.171.137"
HOST = "127.0.0.1"
PORT = 54321


pdb_instance = 100

previous_lineno = -1


watch_dict = {}



class Server(BaseHTTPRequestHandler):


    def do_POST(self):

        if "doc" not in self.__dict__:
            self.doc = xml.dom.minidom.Document()
            self.sa = SA_Control()

        parsed_path = urlparse.urlparse(self.path)

        real_path = parsed_path.path


        if real_path == "/open_state_file":

            f = open("post_info.tmp", "w")

            index = 0
            line = ""
            while True:
                line += self.rfile.readline()
                index += 1
                if "Upload" in line:
                    break

            f.write(line)
            f.close()

            f = open("post_info.tmp", "r")

            form = cgi.FieldStorage(fp=f, headers=self.headers,
                                    environ={'REQUEST_METHOD':'POST',
                                             'CONTENT_TYPE':self.headers['Content-Type'],
                                    })


            for field in form:
                if field == "file":
                    wfile = open("record.pys", "w")
                    wfile.write(form[field].value)
                    break

            f.close()
            if os.path.exists("post_info.tmp"):
	            os.remove("post_info.tmp")


        self.record_statics()

        self.attach_source_code(self.doc.childNodes[0])

        self.attach_console_output(self.doc.childNodes[0])

        self.generate_response()





    def do_GET(self):

        if "doc" not in self.__dict__:

            self.doc = xml.dom.minidom.Document()
            self.sa = SA_Control()



        parsed_path = urlparse.urlparse(self.path)

        real_path = parsed_path.path


        
        target_funcs = []

        
        if real_path == "/record":

            query = parsed_path.query

            query = urllib.unquote(query)


            query_args = query.split("&")


            source_file = ""
            argument = ""
            file_name = ""
            for arg in query_args:
                if arg.startswith("source="):
                    source_file = arg[len("source="):]
                elif arg.startswith("args="):
                    argument = arg[len("args="):]
                elif arg.startswith("file_name="):
                    file_name = arg[len("file_name="):]

                    split_file_names = file_name.split('/')

                    file_name = split_file_names[-1]

                    file_name = file_name[:file_name.find(".")]



            source_file_name = "record.py"
            state_file_name = "record.pys"
			
            self.file_path = source_file_name


            # f = open("record.py", 'w')
            # f.write(source_file)
            # f.close()
            record_file = open(source_file_name, 'w')
            record_file.write(source_file)
            record_file.close()

            original_file = open(file_name + ".py", 'w')
            original_file.write(source_file)
            original_file.close()

            # import_module = "from %s import *" %(file_name)
            # print sys.modules['__main__'].__dict__
            # exec import_module
            # print 'exec %s' %(import_module)
            # sys.modules['__main__'].__setattr__('Foo', None)

            
            if os.path.exists(state_file_name):
                os.remove(state_file_name)


            target_funcs.append([self.sa.start, file_name, self.file_path, state_file_name, True])
            target_funcs.append([self.sa.append_args, argument])
            target_funcs.append([self.sa.init_database])
            target_funcs.append([self.sa.run, True])
            target_funcs.append([self.sa.end, ])
            target_funcs.append([self.record_statics])
        


            for item in target_funcs:
                if item != ():
                    self.execute_request(item[0], item[1:])


            self.attach_console_output(self.doc.childNodes[0])


        elif real_path.startswith("/get_state"):

            args = self.path.split("/")

            id = int(args[2])

            record_elem = self.doc.createElement("record")

            self.doc.appendChild(record_elem)

            hashtable_variables = {}
            self.get_state(record_elem, id, hashtable_variables)


        elif real_path.startswith("/query"):

            query = parsed_path.query

            query = urllib.unquote(query)

            expr = query[len("expr="):]

            self.sa.load("record.pys")

            id_list = self.sa.query_parse(expr)

            record_elem = self.doc.createElement("record")

            for i in id_list:
                bid_elem = self.doc.createElement("bid")

                bid_text_node = self.doc.createTextNode(str(i))

                bid_elem.appendChild(bid_text_node)

                record_elem.appendChild(bid_elem)

            self.doc.appendChild(record_elem)

            



        elif real_path == "/retrieve_state_file":

            f = open("record.pys", "r")

            state_file_content = f.read()

            self.send_response(200)
            self.send_header("Content-type", "application/octet-stream")
            self.send_header("Content-length", str(state_file_content))
            self.end_headers()
            self.wfile.write(state_file_content)
            

            return


        elif real_path == "/debug":

            query = parsed_path.query

            query = urllib.unquote(query)


            query_args = query.split("&")


            command = ""
            source_file = ""
            file_name = ""
            argument = ""


            global watch_dict
            global pdb_instance
            global previous_lineno



            for arg in query_args:

                if arg.startswith("source="):
                    source_file = arg[len("source="):]
                elif arg.startswith("args="):
                    argument = arg[len("args="):]
                elif arg.startswith("file_name="):
                    file_name = arg[len("file_name="):]

                    split_file_names = file_name.split('/')

                    file_name = split_file_names[-1]

                    #file_name = file_name[:file_name.find(".")]
                elif arg.startswith("cmd="):
                    command = arg[len("cmd="):]

                    

            variable_list = []
            backtrace_list = []

            lineno = ""
            command_output = ""
            


            if command == 'start':

                debug_file = open(file_name, 'w')
                debug_file.write(source_file)
                debug_file.close()

                pdb_instance = pexpect.spawn('python -m pdb %s %s' %(file_name, argument))

                command_output = self.send_to_pdb('start_pdb')

                lineno = self.get_lineno_from_command_output(command_output)
                

            elif command == 'r':

                command_output = self.send_to_pdb('r\n')

                lineno = self.get_lineno_from_command_output(command_output, run_model=True)


            elif command.startswith('b'):

                command_output = self.send_to_pdb('%s\n' %(command))


            elif command == 'n':

                command_output = self.send_to_pdb('n\n')

                lineno = self.get_lineno_from_command_output(command_output)



            elif command == 's':

                command_output = self.send_to_pdb('s\n')

                lineno = self.get_lineno_from_command_output(command_output)


            elif command.startswith("clear "):

                command_output = self.send_to_pdb('%s\n' %(command))


            elif command == 'bt':

                pass


            elif command == 'end':

                pdb_instance.close()



            elif command.startswith('p'):

                command_output = self.send_to_pdb('%s\n' %(command))


            elif command.startswith('watch'):

                watch_expr = command[len('watch '):]

                watch_dict[watch_expr] = ""


            elif command == 'remove_all_watch':

                watch_dict = {}


            elif command.startswith('remove_watch'):
                watch_expr = command[len('remove_watch '):]

                if watch_expr in watch_dict:
                    del watch_dict[watch_expr]





            for v in watch_dict:

                pdb_instance.write('print %s\n' %(v))

                watch_output = ""

                try:
                    while True:
                        watch_output += pdb_instance.read_nonblocking(timeout = 0.05)
                except IndexError:
                    watch_output = ""

                except :
                    watch_output = watch_output.split("\n")[1][:-1]


                watch_dict[v] = watch_output



            if lineno == "":
                lineno = previous_lineno

            previous_lineno = lineno


            pdb_instance.write('bt\n')


            bt_output = ""

            try:
                while True:
                    bt_output += pdb_instance.read_nonblocking(timeout = 0.05)
            except:
                pass#print bt_output



            response_elem = self.doc.createElement("response")

            variables_elem = self.doc.createElement("variables")

            for v in watch_dict:
                variable_elem = self.doc.createElement("variable")

                variable_elem.setAttribute("name", str(v))

                variable_elem.setAttribute("value", str(watch_dict[v]))

                variables_elem.appendChild(variable_elem)

            response_elem.appendChild(variables_elem)



            backtrace_list = self.get_backtrace_list(bt_output)

            backtraces_elem = self.doc.createElement("backtraces")

            for b in backtrace_list:
                backtrace_elem = self.doc.createElement("backtrace")

                backtrace_elem.setAttribute("name", str(b[0]))

                backtrace_elem.setAttribute("lineno", str(b[1]))

                backtraces_elem.appendChild(backtrace_elem)

            response_elem.appendChild(backtraces_elem)


            cmd_elem = self.doc.createElement("cmd")

            cmd_text_node = self.doc.createTextNode(command)

            cmd_elem.appendChild(cmd_text_node)

            response_elem.appendChild(cmd_elem)


            lineno_elem = self.doc.createElement("lineno")

            lineno_text_node = self.doc.createTextNode(str(lineno))

            lineno_elem.appendChild(lineno_text_node)

            response_elem.appendChild(lineno_elem)


            console_elem = self.doc.createElement("console")

            console_text_node = self.doc.createTextNode(command_output)

            console_elem.appendChild(console_text_node)

            response_elem.appendChild(console_elem)


            self.doc.appendChild(response_elem)



        self.generate_response()



    def send_to_pdb(self, command):

        global pdb_instance

        if command != 'start_pdb':
            pdb_instance.write(command)

        command_output = ""

        try:
            while True:
                command_output += pdb_instance.read_nonblocking(timeout = 0.05)
        except:
            pass #print command_output

        return command_output





    def attach_source_code(self, root):

        source_elems = self.doc.createElement("source_code")

        source_code = self.sa.get_source_code()

        source_text_node = self.doc.createTextNode(source_code)

        source_elems.appendChild(source_text_node)


        root.appendChild(source_elems)




    def attach_console_output(self, root):

        console_output = self.doc.createElement("console")

        console_text = self.sa.get_console()


        console_text_node = self.doc.createTextNode(console_text)

        console_output.appendChild(console_text_node)


        root.appendChild(console_output)

        


    def get_backtrace_list(self, bt_output):

        lines = bt_output.split('\n')

        bt_list = []

        index = 0
        while True:

            if index >= len(lines):
                return bt_list

            line = lines[index]

            if line.startswith(">"):
                return bt_list



            if line.startswith('->') and not line.startswith('-> exec'):

                previous_line = lines[index - 1]

                lineno = re.findall('\([\d]+\)', previous_line)

                lineno = lineno[0][1:-1]

                name = line[len('-> '):-1]

                bt_list.append([name, lineno])

            index += 1





    def get_lineno_from_command_output(self, command_output, run_model=False):

        if command_output == "":
            return ""

        lines = command_output.split("\n")


        lineno = ""

        if run_model:
            lines.reverse()

        for line in lines:
            if line.startswith('>'):
                lineno = re.findall('\([\d]+\)', line)
                break


        if lineno != []:
            
            lineno = lineno[0][1:-1]

            return lineno



    def generate_response(self):
        self.send_response(200)
        self.send_header("Content-type", "text/xml")

        text = self.doc.toxml()

        #print text
        
        length = len(text)
        self.send_header("Content-length", str(length))
        self.end_headers()
        self.wfile.write(text)



    def record_statics(self):

        self.sa.load("record.pys")

        record_elem = self.doc.createElement("record")

        self.doc.appendChild(record_elem)

        record_elem.setAttribute("size", str(self.sa.get_state_size()))
        hashtable_variables = {}
        self.get_state(record_elem, 0, hashtable_variables)



    def get_state(self, root, id, hashtable_variables):


        self.sa.load("record.pys")

        f_globals, f_locals = self.sa.read_state(id)



        vars_elem = self.doc.createElement("variables")

        root.appendChild(vars_elem)

        for key, value in f_globals.items():

            if key in ["__builtins__", "__package__", "__doc__"]:
                continue


            var_elem = self.doc.createElement("variable")

            var_elem.setAttribute("name", key)
            var_elem.setAttribute("value", str(value))
            var_elem.setAttribute("scope", "global")

            vars_elem.appendChild(var_elem)


        for key, value in f_locals.items():
            if key in ["__builtins__", "__package__", "__doc__"] or key in f_globals:
                continue


            var_elem = self.doc.createElement("variable")

            var_elem.setAttribute("name", key)
            var_elem.setAttribute("value", str(value))
            var_elem.setAttribute("scope", "local")

            vars_elem.appendChild(var_elem)


        console_id = self.sa.get_console_id(id)

        root.setAttribute("console_id", str(console_id))


        source_id = self.sa.get_source_id(id)

        root.setAttribute("lineno", str(source_id))

        root.setAttribute("id", str(id))
                




    def execute_request(self, func, args):

        if args == ():
            func()
        else:
            func(*args)


            


            

if __name__ == '__main__':

    server = HTTPServer((HOST, PORT), Server)
    server.serve_forever()
